//--------------------------------------------------------------------------
// 
//  Copyright (c) Christian Liensberger.  All rights reserved. 
// 
//  Licensed under GPL v2. See official license for details.
// 
//--------------------------------------------------------------------------

using System;
using System.IO;
using System.Runtime.InteropServices;

namespace Chili.Imagizer
{
	/// <summary>
	/// Gets the iamges files from the given list of files and folders.
	/// </summary>
	public sealed class ImagesLoader
	{
		/// <summary>
		/// Fired if an image has been found.
		/// </summary>
		public event EventHandler<ImageLoadedEventArgs> ImageFound;

		private void OnImageFound(ImageLoadedEventArgs e)
		{
			if (ImageFound != null)
				ImageFound(this, e);
		}

		/// <summary>
		/// Gets the image files from the given list of items.
		/// </summary>
		/// <param name="items">The list of items where the files are gotten from.</param>
		/// <param name="recursive">True to follow folders in a recursive way.</param>
		/// <returns></returns>
		public ImagesCollection GetImageFiles(string[] items, bool recursive)
		{
			if (items == null)
				throw new ArgumentNullException("items");

			if (items.Length == 0)
			{
				// Return an image collection with the desktop as root.
				return new ImagesCollection(new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.Desktop)));
			}

			// Holds the result.
			ImagesCollection result = new ImagesCollection(new DirectoryInfo(Path.GetDirectoryName(items[0])));

			foreach (string item in items)
			{
				// Check if the item is a directory.
				if (Directory.Exists(item))
				{
					if (recursive)
						GetDirectoryImageFiles(new DirectoryInfo(item), result);
				}
				else
				{
					FileInfo file = new FileInfo(item);
					// Check if the file is an image.
					if (IsImage(file))
					{
						OnImageFound(new ImageLoadedEventArgs(file));
						result.Add(file);
					}
				}
			}

			return result;
		}

		/// <summary>
		/// Gets the image files in the given directory.
		/// </summary>
		/// <param name="directory">The directory that is inspected for files.</param>
		/// <param name="result">The list holding the results.</param>
		private void GetDirectoryImageFiles(DirectoryInfo directory, ImagesCollection result)
		{
			if (directory == null)
				throw new ArgumentNullException("directory");
			if (result == null)
				throw new ArgumentNullException("result");

			// Add all files.
			foreach (FileInfo file in directory.GetFiles())
			{
				if (IsImage(file))
				{
					OnImageFound(new ImageLoadedEventArgs(file));
					result.Add(file);
				}
			}

			// Add all directories.
			foreach (DirectoryInfo dir in directory.GetDirectories())
			{
				GetDirectoryImageFiles(dir, result);
			}
		}

		/// <summary>
		/// Checks whether the given file is an image.
		/// </summary>
		/// <param name="file">The file that has been given.</param>
		/// <returns></returns>
		private static bool IsImage(FileInfo file)
		{
			if (file == null)
				throw new ArgumentNullException("file");

			if (!file.Exists)
				return false;

			// Try to load the image.
			IntPtr handle = IntPtr.Zero;
			GdipLoadImageFromFile(file.FullName, out handle);

			// Check the result.
			bool result = (handle != IntPtr.Zero);

			// Unload if loading was successful.
			if (result)
				IntGdipDisposeImage(handle);

			return result;
		}

		[DllImport("gdiplus.dll", CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)]
		private static extern int GdipLoadImageFromFile(string filename, out IntPtr image);
		[DllImport("gdiplus.dll", EntryPoint = "GdipDisposeImage", CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)]
		private static extern int IntGdipDisposeImage(IntPtr image);
	}
}
